#ifndef __RENDERER_H__
#define __RENDERER_H__

#include "../Common.h"
#include "../Utils/Array.h"
#include "../Utils/Camera.h"
#include "../Graphics/Color.h"
#include "../Graphics/FontBase.h"
#include "../Graphics/Texture.h"
#include "../Graphics/RenderTarget.h"

namespace RayFrame
{
  class Shader;
  class ShaderProgram;
  class CGEffect;

  #ifdef LINUX
  //#include <GL/glx.h>
  //#include <X11/extensions/xf86vmode.h>
  #endif

  #define MAX_VERTEXSTREAM 32

  typedef unsigned short half;
  typedef unsigned char  ubyte;

  typedef int StreamID;
  typedef int BatchID;
  typedef int VertexFormatID;
  typedef int VertexBufferID;
  typedef int IndexBufferID;
  typedef int TextureID;

  enum Primitives 
  {
    PRIM_TRIANGLES,
    PRIM_TRIANGLE_FAN,
    PRIM_TRIANGLE_STRIP,
    PRIM_QUADS,
    PRIM_LINES,
    PRIM_LINE_STRIP,
    PRIM_LINE_LOOP,
    PRIM_POINTS,
  };

  enum AttributeType
  {
    TYPE_GENERIC = 0,
    TYPE_VERTEX,
    TYPE_TEXCOORD,
    TYPE_NORMAL, 
    TYPE_TANGENT,
    TYPE_BINORMAL,
  };

  enum AttributeFormat
  {
    FORMAT_FLOAT = 0,
    FORMAT_HALF,
    FORMAT_UBYTE,
  };

  enum BufferAccess
  {
    STATIC,
    DEFAULT,
    DYNAMIC,
  };

  struct FormatDesc
  {
    int stream;
    AttributeType type;
    AttributeFormat format;
    int size;
  };

  #define SHADER_NONE     (-1)
  #define TEXTURE_NONE    (-1)
  #define VF_NONE         (-1)
  #define VB_NONE         (-1)
  #define IB_NONE         (-1)

  #define MAX_GENERIC     10
  #define MAX_TEXCOORD    10

  struct VertexBuffer;
  struct IndexBuffer;
  struct VertexFormat;

  class CGShaderProgram;

  class DLL_DECLARE Renderable
  {
  public:
    Renderable(){};

    virtual void Update(double timeElapsed) = 0;
    virtual void BeforeRender() = 0;
    virtual void Render(double timeElapsed) = 0;
    virtual void Render2D(double timeElapsed) = 0;
    virtual void AfterRender() = 0;
  };

  /// translate graphics to directx/opengl.
  class DLL_DECLARE Renderer
  {
  public:
    enum GRAPHIC_TYPE
    {
      OPENGL,
      DIRECTX9,
      DIRECTX10,
    };

    enum RENDER_MODE
    {
      RENDER_POINT,
      RENDER_FILL,
      RENDER_LINE,
      RENDER_MODE_COUNT,
    };

    Renderer();
    virtual ~Renderer();

    virtual GRAPHIC_TYPE GetRendererType() = 0;

    /// Initialization
    virtual bool InitAPI(bool VSync = false) = 0;
    virtual bool Init() = 0;
    virtual void ExitAPI();

    /// Font
    virtual FontBase* AddTextureFont(const char* name, const char *textureFile) = 0;
    virtual FontBase* AddSystemFont(STRING name, STRING fontName, int fontHeight) = 0;

    /// Should be called while window is resized
    virtual void Resize(int width, int height) = 0;

    /// Formats and buffers
    int GetFormatSize(const AttributeFormat format) const;
    virtual VertexFormatID AddVertexFormat(const FormatDesc *formatDesc, const unsigned nAttribs) = 0;
    virtual VertexBufferID AddVertexBuffer(const long size, const BufferAccess bufferAccess, const void *data = NULL) = 0;
    virtual IndexBufferID AddIndexBuffer(const unsigned nIndices, const unsigned indexSize, const BufferAccess bufferAccess, const void *data = NULL) = 0;

    virtual void ChangeVertexFormat(const VertexFormatID vertexFormat) = 0;
    virtual void ChangeVertexBuffer(const int stream, const VertexBufferID vertexBuffer, const int* offset = 0) = 0;
    void ChangeVertexBuffer(const void *base){
      ChangeVertexBuffer(0, VB_NONE, (int*) base);
    }
    virtual void ChangeIndexBuffer(const IndexBufferID indexBuffer) = 0;

    /// Rendering
    virtual void SwapBuffer() = 0;
    virtual void RenderDebugInfo( double timeElapsed ); //< Render infomaiton at right top cornor
    virtual void Clear(Color c = Color(0,0,0,0) ) = 0;  //< Clear the buffer
    virtual void DrawElements(const Primitives primitives, const int firstIndex, const int nIndices, const int firstVertex, const int nVertices) = 0;
    virtual void RenderText(int x, int y, STRING message) = 0;
    virtual void RenderText(int x, int y, const char *string, ...) = 0;
    virtual void Flush() = 0;
    virtual void Finish() = 0;

    /// Return true if no error.
    virtual bool CheckError() = 0;

    // State manipulate functions
    virtual void SetRenderMode(RENDER_MODE rm) = 0;
    virtual void EnableTexture(bool enable = true) = 0;
    virtual void EnableDepthTest(bool enable = true) = 0;

    /// Factory method
    virtual Camera*         CreateCamera() = 0;

    /*  Create texture.
     *  @param param Decide the way texture generate mipmap. If you choose not to generate
     mipmap and use mipmap in your shader, this will lead to a black sampling result.
     *  @name texture name.
     */
    virtual Texture*        CreateTexture(Texture::TexParam_t param = Texture::MIPMAP, 
      STRING name = EMPTY_STRING) = 0;

    virtual RenderTarget*   CreateRenderTarget() = 0;
    virtual ShaderProgram*  CreateShaderProgram() = 0;
    CGShaderProgram*        CreateCGShaderProgram();
    CGEffect*               CreateCGEffect();

    /// Matrix routines
    enum MatrixType
    {
      MATRIX_MODELVIEW,
      MATRIX_PROJECTION,
      MATRIX_TEXTURECOORD,
    };

    virtual void PushMatrix(MatrixType type) = 0;
    virtual void PopMatrix(MatrixType type) = 0;
    virtual void LoadMatrix(glm::mat4 m) = 0;

    virtual void SetColor(Color c) = 0;
  protected:
    FontBase* m_defaultFont;
    Color m_clearColor;

    RefArray <FontBase*> m_fonts;
    RefArray <VertexBuffer> vertexBuffers;
    RefArray <IndexBuffer> indexBuffers;
    RefArray <VertexFormat> vertexFormats;

  protected:
    VertexFormatID currentVertexFormat, selectedVertexFormat;
    VertexBufferID currentVertexBuffers[MAX_VERTEXSTREAM], selectedVertexBuffers[MAX_VERTEXSTREAM];
    const int *selectedOffsets[MAX_VERTEXSTREAM], *currentOffsets[MAX_VERTEXSTREAM];
    IndexBufferID currentIndexBuffer, selectedIndexBuffer;
  public:
    FontBase* GetDefaultFont(){ return m_defaultFont; };

    virtual void SetClearColor(Color c){ m_clearColor = c; };
    Color GetClearColor(){ return m_clearColor; };
  };

  DLL_DECLARE extern Renderer *g_renderer;
  const int MAX_TEXTURE_COUNT = 16;
};

#endif
